Understanding Lambda Expressions

To conclude our look at the .NET event architecture, we will examine C# lambda expressions. As just explained, C# supports the ability to handle events “inline” by assigning a block of code statements directly to an event using anonymous methods, rather than building a stand-alone method to be called by the underlying delegate. Lambda expressions are nothing more than a very concise way to author anonymous methods and ultimately simplify how we work with the .NET delegate type.

To set the stage for our examination of lambda expressions, create a new Console Application named SimpleLambdaExpressions. To begin, consider the FindAll() method of the generic List<T> class. This method can be called when you need to extract out a subset of items from the collection, and is prototyped like so:

// Method of the System.Collections.Generic.List<T> class.
public List<T> FindAll(Predicate<T> match)

As you can see, this method returns a new List<T> that represents the subset of data. Also notice that the sole parameter to FindAll() is a generic delegate of type System.Predicate<T>. This delegate can point to any method returning a bool, and takes a single type parameter as the only input parameter:

// This delegate is used by FindAll() method
// to extract out the subset.
public delegate bool Predicate<T>(T obj);

When you call FindAll(), each item in the List<T> is passed to the method pointed to by the Predicate<T> object. The implementation of said method will perform some calculations to see if the incoming data matches the necessary criteria, and return true or false. If this method returns true, the item will be added to the new List<T> that represents the subset (got all that?)

Before we see how lambda expressions can simplify working with FindAll(), let’s work the problem out in longhand notation, using the delegate objects directly. Add a method (named TraditionalDelegateSyntax()) within your Program type that interacts with the System.Predicate<T> type to discover the even numbers in a List<T> of integers:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("***** Fun with Lambdas *****\n");
        TraditionalDelegateSyntax();

        Console.ReadLine();
    }

    static void TraditionalDelegateSyntax()
    {
        // Make a list of integers.
        List<int> list = new List<int>();
        list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

        // Call FindAll() using traditional delegate syntax.
        Predicate<int> callback = new Predicate<int>(IsEvenNumber);
        List<int> evenNumbers = list.FindAll(callback);

        Console.WriteLine("Here are your even numbers:");
        foreach (int evenNumber in evenNumbers)
        {
            Console.Write("{0}\t", evenNumber);
        }
        Console.WriteLine();
    }

    // Target for the Predicate<> delegate.
    static bool IsEvenNumber(int i)
    {
        // Is it an even number?
        return (i % 2) == 0;
    }
}

Here, we have a method (IsEvenNumber()) that is in charge of testing the incoming integer parameter to see whether it is even or odd via the C# modulo operator, %. If you execute your application, you will find the numbers 20, 4, 8, and 44 print out to the console.

While this traditional approach to working with delegates behaves as expected, the IsEvenNumber() method is invoked only in very limited circumstances—specifically when we call FindAll(), which leaves us with the baggage of a full method definition. If we were to instead use an anonymous method, our code would clean up considerably. Consider the following new method of the Program class:

static void AnonymousMethodSyntax()
{
    // Make a list of integers.
    List<int> list = new List<int>();
    list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

    // Now, use an anonymous method.
    List<int> evenNumbers = list.FindAll(delegate(int i)
    { return (i % 2) == 0; } );

    Console.WriteLine("Here are your even numbers:");
    foreach (int evenNumber in evenNumbers)
    {
        Console.Write("{0}\t", evenNumber);
    }
    Console.WriteLine();
}

In this case, rather than directly creating a Predicate<T> delegate type and then authoring a standalone method, we are able to inline a method anonymously. While this is a step in the right direction, we are still required to use the delegate keyword (or a strongly typed Predicate<T>), and we must ensure that the parameter list is a dead-on match. As well, as you may agree, the syntax used to define an anonymous method can be viewed as being a bit hard on the eyes, which is even more apparent here:

List<int> evenNumbers = list.FindAll(
    delegate(int i)
    {
        return (i % 2) == 0;
    }
);

Lambda expressions can be used to simplify the call to FindAll() even more. When you make use of lambda syntax, there is no trace of the underlying delegate object whatsoever. Consider the following new method to the Program class:

static void LambdaExpressionSyntax()
{
    // Make a list of integers.
    List<int> list = new List<int>();
    list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

    // Now, use a C# lambda expression.
    List<int> evenNumbers = list.FindAll(i => (i % 2) == 0);

    Console.WriteLine("Here are your even numbers:");
    foreach (int evenNumber in evenNumbers)
    {
        Console.Write("{0}\t", evenNumber);
    }
    Console.WriteLine();
}

In this case, notice the rather strange statement of code passed into the FindAll() method, which is in fact a lambda expression. In this iteration of the example, there is no trace whatsoever of the Predicate<T> delegate (or the delegate keyword for that matter). All we have specified is the lambda expression:

i => (i % 2) == 0

Before I break this syntax down, first understand that lambda expressions can be used anywhere you would have used an anonymous method or a strongly typed delegate (typically with far fewer keystrokes). Under the hood, the C# compiler translates the expression into a standard anonymous method making use of the Predicate<T> delegate type (which can be verified using ildasm.exe or reflector.exe). Specifically, the following code statement:

// This lambda expression...
List<int> evenNumbers = list.FindAll(i => (i % 2) == 0);

is compiled into the following approximate C# code:

// ...becomes this anonymous method.
List<int> evenNumbers = list.FindAll(delegate (int i)
This book was purchased by max.sage@webitec.co.uk
{
    return (i % 2) == 0;
});

Dissecting a Lambda Expression

A lambda expression is written by first defining a parameter list, followed by the => token (C#’s token for the lambda operator found in the lambda calculus), followed by a set of statements (or a single statement) that will process these arguments. From a very high level, a lambda expression can be understood as follows:

ArgumentsToProcess => StatementsToProcessThem

Within our LambdaExpressionSyntax() method, things break down like so:

// "i" is our parameter list.
// "(i % 2) == 0" is our statement set to process "i".
List<int> evenNumbers = list.FindAll(i => (i % 2) == 0);

The parameters of a lambda expression can be explicitly or implicitly typed. Currently, the underlying data type representing the i parameter (an integer) is determined implicitly. The compiler is able to figure out that i is an integer based on the context of the overall lambda expression and the underlying delegate. However, it is also possible to explicitly define the type of each parameter in the expression, by wrapping the data type and variable name in a pair of parentheses as follows:

// Now, explicitly state what the parameter type.
List<int> evenNumbers = list.FindAll((int i) => (i % 2) == 0);

As you have seen, if a lambda expression has a single, implicitly typed parameter, the parentheses may be omitted from the parameter list. If you wish to be consistent regarding your use of lambda parameters, you can always wrap the parameter list within parentheses, leaving us with this expression:

List<int> evenNumbers = list.FindAll((i) => (i % 2) == 0);

Finally, notice that currently our expression has not been wrapped in parentheses (we have of course wrapped the modulo statement to ensure it is executed first before the test for equality). Lambda expressions do allow for the statement to be wrapped as follows:

// Now, wrap the expression as well.
List<int> evenNumbers = list.FindAll((i) => ((i % 2) == 0));

Now that you have seen the various ways to build a lambda expression, how can we read this lambda statement in human-friendly terms? Leaving the raw mathematics behind, the following explanation fits the bill:

// My list of parameters (in this case a single integer named i)
// will be processed by the expression (i % 2) == 0.
List<int> evenNumbers = list.FindAll((i) => ((i % 2) == 0));

Processing Arguments Within Multiple Statements

Our first lambda expression was a single statement that ultimately evaluated to a Boolean. However, as you know, many delegate targets must perform a number of code statements. For this reason, C# allows you to build lambda expressions using multiple statement blocks. When your expression must process the parameters using multiple lines of code, you can do so by denoting a scope for these statements using the expected curly brackets. Consider the following example update to our LambdaExpressionSyntax() method:

static void LambdaExpressionSyntax()
{
    // Make a list of integers.
    List<int> list = new List<int>();
    list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

    // Now process each argument within a group of
    // code statements.
    List<int> evenNumbers = list.FindAll((i) =>
    {
        Console.WriteLine("value of i is currently: {0}", i);
        bool isEven = ((i % 2) == 0);
        return isEven;
    });

    Console.WriteLine("Here are your even numbers:");
    foreach (int evenNumber in evenNumbers)
    {
        Console.Write("{0}\t", evenNumber);
    }
    Console.WriteLine();
}

In this case, our parameter list (again, a single integer named i) is being processed by a set of code statements. Beyond the calls to Console.WriteLine(), our modulo statement has been broken into two code statements for increased readability. Assuming each of the methods we’ve looked at in this section are called from within Main():

static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Lambdas *****\n");
    TraditionalDelegateSyntax();
    AnonymousMethodSyntax();
    Console.WriteLine();
    LambdaExpressionSyntax();
    Console.ReadLine();
}

we will find the following output:

***** Fun with Lambdas *****

Here are your even numbers:
20      4      8      44
Here are your even numbers:
20      4      8      44

value of i is currently: 20
value of i is currently: 1
value of i is currently: 4
value of i is currently: 8
value of i is currently: 9
value of i is currently: 44
Here are your even numbers:
20      4      8     44

Source Code The SimpleLambdaExpressions project can be found under the Chapter 11 subdirectory.

Lambda Expressions with Multiple (or Zero) Parameters

The lambda expressions you have seen here processed a single parameter. This is not a requirement, however, as a lambda expression may process multiple arguments—or none. To illustrate the first scenario, create a Console Application named LambdaExpressionsMultipleParams. Next, assume the following incarnation of the SimpleMath type:

public class SimpleMath
{
    public delegate void MathMessage(string msg, int result);
    private MathMessage mmDelegate;

    public void SetMathHandler(MathMessage target)
    {mmDelegate = target; }

    public void Add(int x, int y)
    {
        if (mmDelegate != null)
            mmDelegate.Invoke("Adding has completed!", x + y);
    }
}

Notice that the MathMessage delegate is expecting two parameters. To represent them as a lambda expression, our Main() method might be written as follows:

static void Main(string[] args)
{
    // Register w/ delegate as a lambda expression.
    SimpleMath m = new SimpleMath();
    m.SetMathHandler((msg, result) =>
        {Console.WriteLine("Message: {0}, Result: {1}", msg, result);});

    // This will execute the lambda expression.
    m.Add(10, 10);
    Console.ReadLine();
}

Here, we are leveraging type inference, as our two parameters have not been strongly typed for simplicity. However, we could call SetMathHandler() as follows:

m.SetMathHandler((string msg, int result) =>
    {Console.WriteLine("Message: {0}, Result: {1}", msg, result);});

Finally, if you are using a lambda expression to interact with a delegate taking no parameters at all, you may do so by supplying a pair of empty parentheses as the parameter. Thus, assuming you have defined the following delegate type:

public delegate string VerySimpleDelegate();

you could handle the result of the invocation as follows:

// Prints "Enjoy your string!" to the console.
VerySimpleDelegate d = new VerySimpleDelegate( () => {return "Enjoy your string!";} );
Console.WriteLine(d.Invoke());

Source Code The LambdaExpressionsMultipleParams project can be found under the Chapter 11 subdirectory.

Retrofitting the CarEvents Example Using Lambda Expressions

Given that the whole reason for lambda expressions is to provide a clean, concise manner to define an anonymous method (and therefore indirectly a manner to simplify working with delegates), let’s retrofit the PrimAndProperCarEvents project we created earlier in this chapter. Here is a simplified version of that project’s Program class, which makes use of lambda expression syntax (rather than the raw delegates) to hook into each event sent from the Car object:

class Program
{
    Console.WriteLine("***** More Fun with Lambdas *****\n");

    // Make a car as usual.
    Car c1 = new Car("SlugBug", 100, 10);

    // Hook into events with lambdas!
    c1.AboutToBlow += (sender, e) => { Console.WriteLine(e.msg);};
    c1.Exploded += (sender, e) => { Console.WriteLine(e.msg); };

    // Speed up (this will generate the events).
    Console.WriteLine("\n***** Speeding up *****");
    for (int i = 0; i < 6; i++)
        c1.Accelerate(20);
    
    Console.ReadLine();
}

Hopefully, at this point you can see the overall role of lambda expressions and understand how they provide a "functional manner" to work with anonymous methods and delegate types. Although the new lambda operator (=>) might take a bit to get used to, always remember a lambda expression can be broken down to the following simple equation:

ArgumentsToProcess => StatementsToProcessThem

It is worth pointing out that the LINQ programming model also makes substantial use of lambda expressions to help simplify your coding efforts. You will examine LINQ beginning in Chapter 13.

Source Code The CarDelegateWithLambdas project can be found under the Chapter 11 subdirectory.